﻿using System;
using System.Collections.Generic;
using System.Helpers;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/* 从 BroadcastService部件移植的基类 ，其他协议均为此类的派生类 ，有改动 ， 提供解析报文头部和截取防火盒序列号功能等等 */
namespace DataProcessModule.Protocol
{
    /// <summary>
    /// JT808协议基类
    /// 我们就要能够支持到部标协议的注册，鉴权，gps定位，短消息下发，版本升级，视频频预览，图片抓拍几个核心的功能。
    /// </summary>
    [Serializable]
    public abstract class BaseInstruction
    {
        /// <summary>
        /// 指令前缀
        /// </summary>
        private const byte instPrefix = 0x7e;

        /// <summary>
        /// 指令前后缀
        /// </summary>
        private const byte instSuffix = 0x7e;
        ///// <summary>
        ///// 做为平台下发到终端的指令流水号，每生成一个指令字节流就递增1
        ///// </summary>
        //private static UInt16 autoSN = 0;

        #region 字段
        /// <summary>
        /// 指令头信息
        /// </summary>
        protected InstHead instHeadInfo;

        /// <summary>
        /// 指令分包信息
        /// </summary>
        protected InstMultiPackage instPackageInfo;

        /// <summary>
        /// 指令体
        /// </summary>
        protected byte[] instBodyData;

        /// <summary>
        /// 校验码
        /// </summary>
        private byte checkCode;
        #endregion

        #region 构造函数
        protected BaseInstruction()
        {
        }

        protected BaseInstruction(byte[] buffer)
        {

        }

        protected BaseInstruction(byte[] buffer, ref int offset)
        {

        }

        #endregion

        #region 属性
        /// <summary>
        /// 头信息
        /// </summary>
        public InstHead Head
        {
            get
            {
                return instHeadInfo;
            }
            set
            {
                instHeadInfo = value;
            }
        }

        /// <summary>
        /// 分包信息
        /// </summary>
        public InstMultiPackage PackageInfo
        {
            get
            {
                return instPackageInfo;
            }
            set
            {
                instPackageInfo = value;
            }
        }

        /// <summary>
        /// 是否分包
        /// </summary>
        public bool IsMultiPackage
        {
            get
            {
                return BC.ReadBit(instHeadInfo.instBodyProp, 13);
            }
            set
            {
                BC.WriteBit(ref instHeadInfo.instBodyProp, 13, value);
            }
        }

        /// <summary>
        /// 是否用RSA加密
        /// </summary>
        public bool IsRsa
        {
            get
            {
                return BC.ReadBit(instHeadInfo.instBodyProp, 10);
            }
            set
            {
                BC.WriteBit(ref instHeadInfo.instBodyProp, 10, value);
            }
        }

        /// <summary>
        /// 指令ID
        /// </summary>
        public UInt16 InstructionID
        {
            get
            {
                return instHeadInfo.instID;
            }
            set
            {
                instHeadInfo.instID = value;
            }
        }

        /// <summary>
        /// 终端手机号码（终端ID）
        /// </summary>
        public string TerminalKey
        {
            get
            {
                return instHeadInfo.tdPhoneNumber;
            }
            set
            {
                instHeadInfo.tdPhoneNumber = value;
            }
        }

        /// <summary>
        /// 指令流水号
        /// </summary>
        public UInt16 SN
        {
            get
            {
                return instHeadInfo.instSN;
            }
            set
            {
                instHeadInfo.instSN = value;
            }
        }

        /// <summary>
        /// 指令体属性
        /// </summary>
        public UInt16 InstructionProp
        {
            get
            {
                return instHeadInfo.instBodyProp;
            }
            set
            {
                instHeadInfo.instBodyProp = value;
            }
        }

        /// <summary>
        /// 指令包总数
        /// </summary>
        public UInt16 InstructionPackageCount
        {
            get
            {
                return instPackageInfo.count;
            }
            set
            {
                instPackageInfo.count = value;
            }
        }

        /// <summary>
        /// 指令分包序号
        /// </summary>
        public UInt16 InstructionPackageSN
        {
            get
            {
                return instPackageInfo.sn;
            }
            set
            {
                instPackageInfo.sn = value;
            }
        }

        /// <summary>
        /// 指令体
        /// </summary>
        public byte[] InstructionBodyData
        {
            get
            {
                return instBodyData;
            }
            set
            {
                instBodyData = value;
            }
        }

        /// <summary>
        /// 指令长度
        /// </summary>
        public int InstBodyLength
        {
            get
            {
                return (UInt16)(instHeadInfo.instBodyProp & 0x03FF);
            }
            set
            {
                if (value > 0x03ff)
                    throw new Exception("Instruction body is too long!");
                instHeadInfo.instBodyProp = (UInt16)(instHeadInfo.instBodyProp | (value & 0x03ff));
            }
        }

        #endregion

        #region 方法

        /// <summary>
        /// 得到头信息
        /// </summary>
        public void GetHeadInfo(byte[] buffer, ref int offset)
        {
            instHeadInfo = GetInstructionHead(buffer, ref offset);

            if (IsMultiPackage)
            {
                instPackageInfo = GetMultiPackageInfo(buffer, ref offset);
            }
        }

        /// <summary>
        /// 生成指令字节流
        /// </summary>
        /// <returns></returns>
        public virtual byte[] GenerateInstructionStream()
        {
            //// 先为本条指令生成一个流水号
            //instHeadInfo.instSN = autoSN++;
            try
            {
                // 写入经过转义的指令头 + 指令体 + 校验码
                byte[] checkBody = ConvertFlag();

                byte[] inst = new byte[checkBody.Length + 2];

                //// 用来记录生成指令节流的位置的当前位置
                int streamPos = 0;

                //// 写标识头
                byte[] headOrTailcode = new byte[1];
                headOrTailcode[0] = instPrefix;

                Array.Copy(headOrTailcode, 0, inst, streamPos, headOrTailcode.Length);
                streamPos += headOrTailcode.Length;
                // 写指令体
                Array.Copy(checkBody, 0, inst, streamPos, checkBody.Length);
                streamPos += checkBody.Length;

                //// 写标识尾
                headOrTailcode[0] = instSuffix;
                Array.Copy(headOrTailcode, 0, inst, streamPos, headOrTailcode.Length);
                streamPos += headOrTailcode.Length;

                return inst;
            }
            catch (Exception ex)
            {
                // 写入合成指令流出错日志
                //LOGHelper.WriteLog("BaseInstruction.GenerateInstructionStream()", ex);
            }
            return null;
        }

        /// <summary>
        /// 生成指令头 + 指令体的字节流
        /// </summary>
        /// <returns></returns>
        private byte[] GeneratInstructionHeadAndBody()
        {
            int streamPos = 0;
            //// 写入指令头将指令头转换为字节流
            byte[] head = instHeadInfo.GetHeadStream();

            byte[] multiPackage = null;

            if (IsMultiPackage)
            {
                multiPackage = instPackageInfo.GetSplitPackgeStream();
            }

            byte[] inst = null;
            if (instBodyData != null)
            {
                if (multiPackage != null)
                {
                    inst = new byte[head.Length + instBodyData.Length + multiPackage.Length];
                }
                else
                {
                    inst = new byte[head.Length + instBodyData.Length];
                }
            }
            else
                inst = new byte[head.Length];
            Array.Copy(head, 0, inst, streamPos, head.Length);
            streamPos += head.Length;

            if (IsMultiPackage)
            {
                Array.Copy(multiPackage, 0, inst, streamPos, multiPackage.Length);
                streamPos += multiPackage.Length;
            }

            if (instBodyData != null)
            {
                // 写入指令体
                Array.Copy(instBodyData, 0, inst, streamPos, instBodyData.Length);
                streamPos += instBodyData.Length;
            }
            return inst;
        }

        /// <summary>
        /// 返回经过异或运算的 指令关 + 指令体 + 校验码
        /// </summary>
        /// <returns></returns>
        private byte[] GenerateInstInstructionCheckBody()
        {
            byte[] instSub = GeneratInstructionHeadAndBody();
            byte[] inst = new byte[instSub.Length + 1];

            byte checkCode = Utilities.StreamXOR(instSub);
            Array.Copy(instSub, 0, inst, 0, instSub.Length);
            inst[inst.Length - 1] = checkCode;

            return inst;
        }

        /// <summary>
        /// 执行转义
        /// </summary>
        /// <returns></returns>
        private byte[] ConvertFlag()
        {
            byte[] inst = GenerateInstInstructionCheckBody();

            int count = 0;
            for (int i = 0; i < inst.Length; i++)
            {
                if (inst[i] == 0x7e || inst[i] == 0x7d)
                {
                    count++;
                }
            }

            //// 不需要转议
            if (count == 0)
            {
                return inst;
            }

            byte[] subinst = new byte[inst.Length + count];

            int j = 0;
            for (int i = 0; i < inst.Length; i++)
            {
                if (j < subinst.Length)
                {
                    if (inst[i] == 0x7e)
                    {
                        subinst[j] = 0x7d;
                        subinst[++j] = 0x02;
                    }
                    else if (inst[i] == 0x7d)
                    {
                        subinst[j] = 0x7d;
                        subinst[++j] = 0x01;
                    }
                    else
                    {
                        subinst[j] = inst[i];
                    }

                    j++;
                }
            }

            return subinst;
        }

        /// <summary>
        /// 获取指令头信息
        /// </summary>
        /// <param name="buffer">指令节字流</param>
        /// <returns>指令头</returns>
        public static InstHead GetInstructionHead(byte[] buffer, ref int offset)
        {
            InstHead head = new InstHead();
            head.instID = BC.ToUInt16Net(buffer, ref offset);
            head.instBodyProp = BC.ToUInt16Net(buffer, ref offset);
            offset++;
            head.tdPhoneNumber = BC.ToBCDString(buffer, ref offset, 4);
            //offset++;
            head.instSN = BC.ToUInt16Net(buffer, ref offset);

            return head;
        }

        /// <summary>
        /// 获取分包信息
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        private InstMultiPackage GetMultiPackageInfo(byte[] buffer, ref int offset)
        {
            InstMultiPackage packageinfo = new InstMultiPackage();
            packageinfo.count = BC.ToUInt16Net(buffer, ref offset);
            packageinfo.sn = BC.ToUInt16Net(buffer, ref offset);
            return packageinfo;
        }

        /// <summary>
        /// 获取通道号
        /// </summary>
        /// <param name="channelNoMt"></param>
        public static byte GetChannelNo(byte channelNoMt)
        {
            byte channelNo = 0x01;
            // zt 通过比特位（默认格式为复合多通道）
            for (int i = 1; i < 9; i++)
            {
                if ((channelNoMt & 0x01) == 0x01)
                {
                    channelNo = (byte)i;
                    break;
                }
                else
                    channelNoMt = (byte)(channelNoMt >> 1);
            }
            return channelNo;
        }

        #endregion
    }

    #region 结构
    /// <summary>
    /// 指令头
    /// </summary>
    [Serializable]
    public struct InstHead
    {
        /// <summary>
        /// 指令ID
        /// </summary>
        public UInt16 instID;
        /// <summary>
        /// 指令体消息属性
        /// </summary>
        public UInt16 instBodyProp;
        /// <summary>
        /// 终端手机号码
        /// </summary>
        public string tdPhoneNumber;
        /// <summary>
        /// 指令流水号
        /// </summary>
        public UInt16 instSN;

        /// <summary>
        /// 得到BigEndian指令头码流
        /// </summary>
        /// <returns></returns>
        public byte[] GetHeadStream()
        {
            if (tdPhoneNumber == null)
            {
                tdPhoneNumber = string.Empty;
            }

            byte[] bcdCode = BC.FromBCDString(tdPhoneNumber);
            byte[] stream = new byte[3 * sizeof(UInt16) + bcdCode.Length + 1];

            int offset = 0;

            BC.WriteNet(instID, stream, ref offset);
            BC.WriteNet(instBodyProp, stream, ref offset);
            //Edit By Nergigante 2019-01-08
            //补充一个版本号信息
            stream[offset++] = 0x2;
            BC.Write(bcdCode, stream, ref offset);
            BC.WriteNet(instSN, stream, ref offset);

            return stream;
        }
    }

    /// <summary>
    /// 指令分包信息
    /// </summary>
    [Serializable]
    public struct InstMultiPackage
    {
        /// <summary>
        /// 分包总数
        /// </summary>
        public UInt16 count;
        /// <summary>
        /// 分包序号
        /// </summary>
        public UInt16 sn;

        /// <summary>
        /// 得到BigEndian指令头码流
        /// </summary>
        /// <returns></returns>
        public byte[] GetSplitPackgeStream()
        {
            try
            {
                byte[] stream = new byte[2 * sizeof(UInt16)];

                int offset = 0;

                BC.WriteNet(count, stream, ref offset);
                BC.WriteNet(sn, stream, ref offset);

                return stream;
            }
            catch
            {
                return null;
            }
        }
    }
    #endregion
}
